package net.esj.basic.dao.hibernate.sessioninterceptor;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

import net.esj.basic.core.business.provider.redundant.RedundantFactory;
import net.esj.basic.dao.Dao;
import net.esj.basic.dao.DaoProxy;
import net.esj.basic.pojo.AbstractPojo;
import net.esj.basic.pojo.MObject;
import net.esj.basic.pojo.UpdateType;
import net.esj.basic.pojo.Updater;
import net.esj.basic.utils.JavaProtoTypeHelper;

import org.apache.commons.lang.StringUtils;
import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component("dirtyFindInterceptor")
@Scope("prototype")
public class DirtyFindInterceptor extends EmptyInterceptor {

	@Override
	public int[] findDirty(Object entity, Serializable id,
			Object[] currentState, Object[] previousState,
			String[] propertyNames, Type[] types) {
		if(entity instanceof AbstractPojo){
			Updater updater = ((AbstractPojo)entity).getUpdater();
			UpdateType type =((AbstractPojo)entity).getUpdateType();
			if(type==UpdateType.AUTO){
				type = checkUpdateType(entity, id, currentState, previousState, propertyNames, types);
			}
			switch(type){
			case BY_DIFF:
				return findDirtyByDiff(entity, id, currentState, previousState, propertyNames, types);
			case BY_UPDATER:
				return findDirtyByUpdater(entity, id, currentState, previousState, propertyNames, types, ((AbstractPojo)entity));
			case NONE:
				return super.findDirty(entity, id, currentState, previousState, propertyNames,
						types);
			}
		}
		return super.findDirty(entity, id, currentState, previousState, propertyNames,
				types);
		
	}
	
	protected UpdateType checkUpdateType(Object entity, Serializable id,
			Object[] currentState, Object[] previousState,
			String[] propertyNames, Type[] types){
		if(! (entity instanceof AbstractPojo)){
			return UpdateType.NONE;
		}
		Updater updater = ((AbstractPojo)entity).getUpdater();
		if(updater.hasProperty()){
			return UpdateType.BY_UPDATER;
		}
		return UpdateType.NONE;
	}
 
	private int[] findDirtyByUpdater(Object entity, Serializable id,
			Object[] currentState, Object[] previousState,
			String[] propertyNames, Type[] types,AbstractPojo pojo){
		Updater updater = pojo.getUpdater();
		Set<Integer> tmp = new HashSet<Integer>(); 
		//int[] reval = new int[propertyNames.length];
		for(int i=0;i<propertyNames.length;i++){
			String propertyName = propertyNames[i];
			if(updater.getProperties().contains(propertyName)){
				tmp.add(i);
			}
		}
		int[] reval = new int[tmp.size()];
		int i=0;
		for(Integer t:tmp){
			reval[i] = t;
			i++;
		}
		return reval;
	}
	
	private int[] findDirtyByDiff(Object entity, Serializable id,
			Object[] currentState, Object[] previousState,
			String[] propertyNames, Type[] types){
		if(currentState==null
				|| previousState ==null){
			return null;
		}
		
		Set<Integer> tmp = new HashSet<Integer>(); 
		for(int i=0;i<currentState.length;i++){
			Object cur = currentState[i];
			Object pre = previousState[i];
			if(!JavaProtoTypeHelper.equal(cur, pre)){
				tmp.add(i);
			}
		}
		int[] reval = new int[tmp.size()];
		int i=0;
		for(Integer t:tmp){
			reval[i] = t;
			i++;
		}
		return reval;
	}
	
	private Dao dao;
	
	@Override
	public boolean onFlushDirty(Object entity, Serializable id,
			Object[] currentState, Object[] previousState,
			String[] propertyNames, Type[] types) {
		if(entity instanceof MObject){
			MObject oldt = (MObject) entity;
			MObject newt = (MObject) ((AbstractPojo)entity).self;
			if(newt==null){
				newt = (MObject) getDao().get(id, entity.getClass());
			}
			if(oldt!=null && newt!=null && StringUtils.equals(oldt.getName(), newt.getName())){
				RedundantFactory.getInstance().doRedundant((MObject) entity);
			}
		}
		return super.onFlushDirty(entity, id, currentState, previousState,
				propertyNames, types);
	}
	
	
	private Dao getDao(){
		if(dao==null){
			dao =DaoProxy.getProxy(Dao.class);
		}
		return dao;
	}
}
